﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace AgentTest
{
    public class CachedBufferEntry
    {
        private byte[] m_buffer = null;
        public byte[] Buffer { get { return m_buffer; } }

        private bool m_inUse = false;
        public bool InUse
        {
            get { lock (this) { return m_inUse; } }
            set { lock (this) { m_inUse = value; } }
        }

        public bool Capture()
        {
            lock (this)
            {
                if (!m_inUse)
                {
                    m_inUse = true;
                    return true;
                }
                return false;
            }
        }

        public CachedBufferEntry(byte[] buffer)
        {
            m_buffer = buffer;
        }
        public CachedBufferEntry(int size)
        {
            m_buffer = new byte[size];
        }

        public static explicit operator byte[](CachedBufferEntry buffer)
        {
            return buffer.Buffer;
        }

    }

    public class CachedBuffer : IDisposable
    {
        private CachedBufferEntry m_entry = null;

        public byte[] Buffer { get { return m_entry.Buffer; } }

        public CachedBuffer(CachedBufferEntry entry)
        {
            this.m_entry = entry;
        }

        public static explicit operator byte[](CachedBuffer buffer)
        {
            return buffer.Buffer;
        }

        private bool m_disposed = false;
        protected virtual void Dispose(bool dispoing)
        {
            if (!m_disposed)
            {
                if (dispoing)
                {
                    m_entry.InUse = false;
                    m_entry = null;
                }
                m_disposed = true;
            }
        }

        ~CachedBuffer()
        {
            Dispose(false);
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
    }

    public static class CachedBufferAllocator
    {
        class Bucket
        {
            private List<CachedBufferEntry> m_bits = new List<CachedBufferEntry>();
            public int Size { get; private set; }

            public Bucket(int bucketSize)
            {
                Size = bucketSize;
            }

            public CachedBufferEntry Get()
            {
                for (int x = 0; x < m_bits.Count; x++)
                {
                    if (m_bits[x].Capture())
                    {
                        return m_bits[x];//return
                    }
                }
                //add
                CachedBufferEntry entry = new CachedBufferEntry(Size);
                entry.InUse = true;
                m_bits.Add(entry);
                return entry;
            }

            public int CalculateAllocatedBytes()
            {
                int inUse = 0;
                for (int x = 0; x < m_bits.Count; x++)
                {
                    if (m_bits[x].InUse)
                    {
                        inUse++;
                    }
                }
                return Size * inUse;
            }
        }

        private static object m_mutex = new object();
        private static List<Bucket> m_buckets = new List<Bucket>();

        private const int BUCKET_WIDTH = 128 * 1024;
        private const int INITIAL_BUCKET_COUNT = 10;
        private const int MAX_USED_BEFORE_COLLECTION = 512 * 1024 * 1024;

        static CachedBufferAllocator()
        {
            int curBucketSize = BUCKET_WIDTH;
            for (int x = 0; x < INITIAL_BUCKET_COUNT; x++)
            {
                m_buckets.Add(new Bucket(curBucketSize));
                curBucketSize += BUCKET_WIDTH;
            }

        }
    }

    //public class CachedBufferCollection : IDisposable
    //{
    //    private List<CachedBufferEntry> m_buffers = new List<CachedBufferEntry>();

    //    public byte[] Get(int size)
    //    {
    //        lock (this)
    //        {
    //            CachedBufferEntry entry= ca
    //        }
    //    }
    //}

}
